<?php
/**
 * Nweb Framework
 *
 * This script is protected by copyright. It's use, copying, modification
 * and distribution without written consent of the author is prohibited.
 *
 * @category    source
 * @package     pl.nweb.framework.response
 * @subpackage  pl.nweb.framework.response.http
 * @author      Krzysztof Kardasz <krzysztof.kardasz@gmail.com>
 * @copyright   Copyright (c) 2011 Krzysztof Kardasz
 * @license     http://www.gnu.org/licenses/lgpl-3.0.txt  GNU Lesser General Public
 * @version     3.0 $Id$
 * @link        http://code.google.com/p/nweb-framework/
 * @link        http://framework.nweb.pl
 */

namespace pl\nweb\framework\response;

/**
 * Odpowiedź HTTP
 *
 * @category    source
 * @package     pl.nweb.framework.response
 * @subpackage  pl.nweb.framework.response.http
 * @author      Krzysztof Kardasz <krzysztof.kardasz@gmail.com>
 * @copyright   Copyright (c) 2011 Krzysztof Kardasz
 * @version     3.0 $Id$
 */
class Http extends \pl\nweb\framework\Response
{
    /**
     * Kody odpowiedzi protokołu HTTP 1.1
     *
     * @link http://www.faqs.org/rfcs/rfc2616
     * @var integer
     */
     const OK = 200;
     const CREATED = 201;
     const ACCEPTED = 202;
     const NON_AUTHORITATIVE_INFORMATION = 203;
     const NO_CONTENT = 204;
     const RESET_CONTENT = 205;
     const PARTIAL_CONTENT = 206;
     const MULTIPLE_CHOICES = 300;
     const MOVED_PERMANENTLY = 301;
     const FOUND = 302;
     const SEE_OTHER = 303;
     const NOT_MODIFIED = 304;
     const USE_PROXY = 305;
     const TEMPORARY_REDIRECT = 307;
     const BAD_REQUEST = 400;
     const UNAUTHORIZED = 401;
     const PAYMENT_REQUIRED = 402;
     const FORBIDDEN = 403;
     const NOT_FOUND = 404;
     const METHOD_NOT_ALLOWED = 405;
     const NOT_ACCEPTABLE = 406;
     const PROXY_AUTHENTICATION_REQUIRED = 407;
     const REQUEST_TIMEOUT = 408;
     const CONFLICT = 409;
     const GONE = 410;
     const LENGTH_REQUIRED = 411;
     const PRECONDITION_FAILED = 412;
     const REQUEST_ENTITY_TOO_LARGE = 413;
     const REQUEST_URI_TOO_LONG = 414;
     const UNSUPPORTED_MEDIA_TYPE = 415;
     const REQUESTED_RANGE_NOT_SATISFIABLE = 416;
     const EXPECTATION_FAILED = 417;
     const INTERNAL_SERVER_ERROR = 500;
     const NOT_IMPLEMENTED = 501;
     const BAD_GATEWAY = 502;
     const SERVICE_UNAVAILABLE = 503;
     const GATEWAY_TIMEOUT = 504;
     const HTTP_VERSION_NOT_SUPPORTED = 505;

    /**
     * Treść odpowiedzi
     *
     * @var null|string
     */
    protected $_contents = null;

    /**
     * Nagłówki odpowiedzi
     *
     * @var array
     */
    protected $_headers = array();

    /**
     * Zwracany kod odpowiedzi protokołu HTTP 1.1
     *
     * @var integer
     */
    protected $_code = self::OK;

    /**
     * Lista kodów odpowiedzi protokołu HTTP 1.1
     *
     * @link http://www.faqs.org/rfcs/rfc2616
     * @var array
     */
    protected static $_codes = array(
        self::OK => 'OK',
        self::CREATED => 'Created',
        self::ACCEPTED => 'Accepted',
        self::NON_AUTHORITATIVE_INFORMATION => 'Non-Authoritative Information',
        self::NO_CONTENT => 'No Content',
        self::RESET_CONTENT => 'Reset Content',
        self::PARTIAL_CONTENT => 'Partial Content',
        self::MULTIPLE_CHOICES => 'Multiple Choices',
        self::MOVED_PERMANENTLY => 'Moved Permanently',
        self::FOUND => 'Found',
        self::SEE_OTHER => 'See Other',
        self::NOT_MODIFIED => 'Not Modified',
        self::USE_PROXY => 'Use Proxy',
        self::TEMPORARY_REDIRECT => 'Temporary Redirect',
        self::BAD_REQUEST => 'Bad Request',
        self::UNAUTHORIZED => 'Unauthorized',
        self::PAYMENT_REQUIRED => 'Payment Required',
        self::FORBIDDEN => 'Forbidden',
        self::NOT_FOUND => 'Not Found',
        self::METHOD_NOT_ALLOWED => 'Method Not Allowed',
        self::NOT_ACCEPTABLE => 'Not Acceptable',
        self::PROXY_AUTHENTICATION_REQUIRED => 'Proxy Authentication Required',
        self::REQUEST_TIMEOUT => 'Request Timeout',
        self::CONFLICT => 'Conflict',
        self::GONE => 'Gone',
        self::LENGTH_REQUIRED => 'Length Required',
        self::PRECONDITION_FAILED => 'Precondition Failed',
        self::REQUEST_ENTITY_TOO_LARGE => 'Request Entity Too Large',
        self::REQUEST_URI_TOO_LONG => 'Request-URI Too Long',
        self::UNSUPPORTED_MEDIA_TYPE => 'Unsupported Media Type',
        self::REQUESTED_RANGE_NOT_SATISFIABLE => 'Requested Range Not Satisfiable',
        self::EXPECTATION_FAILED => 'Expectation Failed',
        self::INTERNAL_SERVER_ERROR => 'Internal Server Error',
        self::NOT_IMPLEMENTED => 'Not Implemented',
        self::BAD_GATEWAY => 'Bad Gateway',
        self::SERVICE_UNAVAILABLE => 'Service Unavailable',
        self::GATEWAY_TIMEOUT => 'Gateway Timeout',
        self::HTTP_VERSION_NOT_SUPPORTED => 'HTTP Version Not Supported'
    );

    /**
     * Definuje kod odpowiedzi protokołu HTTP 1.1
     *
     * @param  integer $code kod odpowiedzi protokołu HTTP 1.1
     * @return \pl\nweb\framework\response\Http
     * @throws \pl\nweb\framework\response\http\Exception
     */
    public function setCode ($code)
    {
        if(array_key_exists($code, self::$_codes)) {
            $this->_code = $code;
        } else {
            throw new http\Exception ('Incorrect http response code ' . $code);
        }
        return $this;
    }

    /**
     * Definiuje nagłówek odpowiedzi
     *
     * @param  string $name nazwa nagłówka
     * @param  string $value wartość nagłówka
     * @param  boolean $replace flaga czy zastąpić poprzedni nagłówek
     * @return \pl\nweb\framework\response\Http
     */
    public function setHeader ($name, $value, $replace = false)
    {
        if($replace || !isset($this->_headers[$name])) {
            $this->_headers[$name] = array($value, $replace);
        }
        return $this;
    }

    /**
     * Usuwa nagłówek odpowiedzi
     *
     * @param  string $name nazwa nagłówka
     * @return \pl\nweb\framework\response\Http
     */
    public function unsetHeader ($name)
    {
        if(isset($this->_headers[$name])) {
            unset($this->_headers[$name]);
        }
        return $this;
    }

    /**
     * Zwraca flagę czy nagłówek istnieje
     *
     * @param  string $name nazwa nagłówka
     * @return boolean
     */
    public function hasHeader ($name)
    {
        return isset($this->_headers[$name]);
    }

    /**
     * Zwraca flagę czy nagłówki mogą zostać wysłane
     *
     * @return boolean
     */
    public function canSendHeaders ()
    {
        return !headers_sent();
    }

    /**
     * Wysyła nagłówki odpowiedzi
     *
     * @return \pl\nweb\framework\response\Http
     */
    public function sendHeaders ()
    {
        if(headers_sent()) {
            throw new http\Exception ('Headers already sent');
        }

        foreach($this->_headers as $name => $value) {
            header(($name . ': ' . $value[0]), $value[1]);
        }

        if($this->_code != 200) {
            header('HTTP/1.1 ' . $this->_code);
        }

        return $this;
    }


    /**
     * Definuje treść odpowiedzi
     *
     * @param  mixed $content odpowiedź
     * @return \Nweb\Response
     */
    public function setContents ($content)
    {
        $this->_contents = $content;
        return $this;
    }

    /**
     * Zwraca treść dpowiedzi
     *
     * @return mixed
     */
    public function getContents ()
    {
        return $this->_contents;
    }

    /**
     * Zwraca treść dpowiedzi
     *
     * @return boolean
     */
    public function hasContents ()
    {
        return null !== $this->_contents;
    }

    /**
     * Zwraca treść dpowiedzi
     *
     * @return boolean
     */
    public function clear ()
    {
        $this->_headers  = array();
        $this->_contents = null;
        $this->_code     = 200;
    }

    /**
     * Wysyła odpowiedź aplikacji
     *
     * @return void
     */
    public function send ()
    {
        $this->sendHeaders();
        echo $this->getContents();
    }
}